#region Imports

using System;
using System.Collections.Generic;
using Backend.Domain;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

#endregion

namespace Backend.Services.SP2007
{

    /// <summary>
    /// Provides column ordering services for SharePoint 2007.
    /// </summary>
    internal sealed class SP2007ColumnOrderingService : ColumnOrderingService
    {

        #region Methods - Static Member

        /// <summary>
        /// Finds the site identified by the given identifier, in the specified application.
        /// </summary>
        /// <param name="application">
        /// The application in which the site identified by <paramref name="siteId"/> resides.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site to be found.
        /// </param>
        /// <returns>
        /// The site identified by <paramref name="siteId"/>, 
        /// within <paramref name="application"/>.
        /// </returns>
        private static SPSite FindSite(SPWebApplication application, Guid siteId)
        {
            SPSite site = null;

            SPSiteCollection siteCollections = application.Sites;

            // TODO: refactor to remove loop
            foreach (SPSite currentSite in siteCollections)
            {
                if (currentSite.ID == siteId)
                {
                    site = currentSite;
                    break;
                }
            }

            return site;
        }

        #endregion

        #region Methods - Instance Member

        #region Methods - Instance Member - IColumnOrderingService Members

        /// <summary>
        /// Gets the applications in the SharePoint instance.
        /// </summary>
        /// <returns>
        /// The applications in the SharePoint instance.
        /// </returns>
        public override ApplicationCollection GetApplications()
        {
            ApplicationCollection applicationCollection = null;

            applicationCollection = ApplicationConverter.Convert(
                SPWebService.ContentService.WebApplications);

            return applicationCollection;
        }

        /// <summary>
        /// Gets the application context in current use.
        /// </summary>
        /// <returns>
        /// The application context in current use.
        /// </returns>
        public override Application GetApplication()
        {
            Application application = null;

            application = ApplicationConverter.Convert(SPContext.Current.Web.Site.WebApplication);

            return application;
        }

        /// <summary>
        /// Gets the application identified by the given identifier.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application to be retrieved.
        /// </param>
        /// <returns>
        /// The application identified by <paramref name="applicationId"/>.
        /// </returns>
        public override Application GetApplication(Guid applicationId)
        {
            Application application = null;

            SPWebApplication sharepointApplication = SPWebService.ContentService.WebApplications[
                applicationId];

            application = ApplicationConverter.Convert(sharepointApplication);

            return application;
        }

        /// <summary>
        /// Gets the sites for the specified application identifier.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application whose sites will be retrieved.
        /// </param>
        /// <returns>
        /// The sites owned by the application identified by <paramref name="applicationId"/>.
        /// </returns>
        public override SiteCollection GetSites(Guid applicationId)
        {
            SiteCollection siteCollection = null;

            SPWebApplication application = SPWebService.ContentService.WebApplications[
                applicationId];
            
            siteCollection = SiteConverter.Convert(application.Sites);

            return siteCollection;
        }

        /// <summary>
        /// Gets the site context in current use.
        /// </summary>
        /// <returns>
        /// The site context in current use.
        /// </returns>
        public override Site GetSite()
        {
            Site site = null;

            SPSite sharepointSite = SPContext.Current.Web.Site;

            site = SiteConverter.Convert(sharepointSite);

            return site;
        }

        /// <summary>
        /// Gets the site with the specified identifier, within the application identified
        /// by the given identifier.
        /// </summary>
        /// <param name="applicationId">
        /// The application identifier.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the application containing the site.
        /// </param>
        /// <returns>
        /// The site identified by <paramref name="siteId"/>, within the application identified
        /// by <paramref name="siteId"/>.
        /// </returns>
        public override Site GetSite(Guid applicationId, Guid siteId)
        {
            Site site = null;

            SPWebApplication sharepointApplication = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite sharepointSite = SP2007ColumnOrderingService.FindSite(
                sharepointApplication,
                siteId);

            site = SiteConverter.Convert(sharepointSite);

            return site;
        }

        /// <summary>
        /// Gets the webs for the given site, in the specified application.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application containing the site whose webs will be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site containing the web whose lists will be retrieved.
        /// </param>
        /// <returns>
        /// The webs owned by the specified site, in the specified application.
        /// </returns>
        public override WebCollection GetWebs(
            Guid applicationId,
            Guid siteId)
        {
            WebCollection webCollection = null;

            SPWebApplication application = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite site = SP2007ColumnOrderingService.FindSite(application, siteId);

            webCollection = WebConverter.Convert(site.AllWebs);

            return webCollection;
        }

        /// <summary>
        /// Gets the web for the specified site identifier.
        /// </summary>
        /// <param name="siteId">
        /// The identifier of the site whose webs will be retrieved.
        /// </param>
        /// <returns>
        /// The webs owned by the site identified by <paramref name="siteId"/>.
        /// </returns>
        public override WebCollection GetWebs(Guid siteId)
        {
            WebCollection webCollection = null;

            SPWebApplication application = SPContext.Current.Web.Site.WebApplication;
            SPSiteCollection siteCollections = application.Sites;
            SPSite site = SP2007ColumnOrderingService.FindSite(application, siteId);

            webCollection = WebConverter.Convert(site.AllWebs);

            return webCollection;
        }

        /// <summary>
        /// Gets the web context in current use.
        /// </summary>
        /// <returns>
        /// The web context in current use.
        /// </returns>
        public override Web GetWeb()
        {
            Web web = null;

            SPWeb sharepointWeb = SPContext.Current.Web;

            web = WebConverter.Convert(sharepointWeb);

            return web;
        }

        /// <summary>
        /// Gets web, in the specified site, in the specified application.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application containing the web to be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site containing the web to be retrieved.
        /// </param>
        /// <param name="webId">
        /// The identifier of the web to be retrieved.
        /// </param>
        /// <returns>
        /// The web in the specified site, in the specified application.
        /// </returns>
        public override Web GetWeb(
            Guid applicationId,
            Guid siteId,
            Guid webId)
        {
            Web web = null;

            SPWebApplication sharepointApplication = SPContext.Current.Web.Site.WebApplication;
            SPSiteCollection sharepointSiteCollections = sharepointApplication.Sites;
            SPSite sharepointSite = SP2007ColumnOrderingService.FindSite(
                sharepointApplication, siteId);
            SPWeb sharepointWeb = sharepointSite.AllWebs[webId];

            web = WebConverter.Convert(sharepointWeb);

            return web;
        }

        /// <summary>
        /// Gets the views for the given web, in the specified site, in the specified application.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application containing the web whose lists will be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site containing the web whose lists will be retrieved.
        /// </param>
        /// <param name="webId">
        /// The identifier of the web containing the web whose lists will be retrieved.
        /// </param>
        /// <returns>
        /// The views owned by the specified web, 
        /// in the specified site, in the specified application.
        /// </returns>
        public override ListCollection GetLists(
            Guid applicationId,
            Guid siteId,
            Guid webId)
        {
            ListCollection listCollection = null;

            SPWebApplication application = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite site = SP2007ColumnOrderingService.FindSite(application, siteId);
            SPWeb web = site.AllWebs[webId];

            listCollection = ListConverter.Convert(web.Lists);

            return listCollection;
        }

        /// <summary>
        /// Gets the lists for the specified web identifier.
        /// </summary>
        /// <param name="webId">
        /// The identifier of the web whose lists will be retrieved.
        /// </param>
        /// <returns>
        /// The lists owned by the web identified by <paramref name="webId"/>.
        /// </returns>
        public override ListCollection GetLists(Guid webId)
        {
            ListCollection listCollection = null;

            SPWeb web = SPContext.Current.Web.Site.AllWebs[webId];

            listCollection = ListConverter.Convert(web.Lists);

            return listCollection;
        }

        /// <summary>
        /// Gets the specified list in the specified web, in the specified site, 
        /// in the specified application.
        /// </summary>
        /// <param name="applicationId">
        /// The application containing the list to be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The site containing the list to be retrieved.
        /// </param>
        /// <param name="webId">
        /// The web containing the list to be retrieved.
        /// </param>
        /// <param name="listId">
        /// The list to be retrieved.
        /// </param>
        /// <returns>
        /// The list with the give identifier, in the specified web, 
        /// in the specified site, in the specified application.
        /// </returns>
        public override List GetList(
            Guid applicationId,
            Guid siteId,
            Guid webId,
            Guid listId)
        {
            List list = null;

            SPWebApplication sharepointApplication = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite sharepointSite = SP2007ColumnOrderingService.FindSite(
                sharepointApplication, siteId);
            SPWeb sharepointWeb = sharepointSite.AllWebs[webId];
            SPList sharepointList = sharepointWeb.Lists[listId];

            list = ListConverter.Convert(sharepointList);

            return list;
        }

        /// <summary>
        /// Gets the views for the given list, 
        /// in the specified web, in the specified site, in the specified application.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application containing the list whose views will be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site containing the list whose views will be retrieved.
        /// </param>
        /// <param name="webId">
        /// The identifier of the web containing the list whose views will be retrieved.
        /// </param>
        /// <param name="listId">
        /// The identifier of the list whose views will be retrieved.
        /// </param>
        /// <returns>
        /// The views owned by the specified list, 
        /// in the specified web, in the specified site, in the specified application.
        /// </returns>
        public override ViewCollection GetViews(
            Guid applicationId,
            Guid siteId,
            Guid webId,
            Guid listId)
        {
            ViewCollection viewCollection = null;

            SPWebApplication application = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite site = SP2007ColumnOrderingService.FindSite(application, siteId);
            SPWeb web = site.AllWebs[webId];
            SPList list = web.Lists[listId];

            viewCollection = ViewConverter.Convert(list.Views);

            return viewCollection;
        }

        /// <summary>
        /// Gets the views for the specified list identifier.
        /// </summary>
        /// <param name="listId">
        /// The identifier of the list whose views will be retrieved.
        /// </param>
        /// <returns>
        /// The views owned by the list identified by <paramref name="listId"/>.
        /// </returns>
        public override ViewCollection GetViews(Guid listId)
        {
            ViewCollection viewCollection = null;

            SPList list = SPContext.Current.Web.Lists[listId];

            viewCollection = ViewConverter.Convert(list.Views);

            return viewCollection;
        }

        /// <summary>
        /// Gets the view with the specified identifier, for the given list, 
        /// in the specified web, in the specified site, in the specified application.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application containing the view to be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site containing the view to be retrieved.
        /// </param>
        /// <param name="webId">
        /// The identifier of the web containing the view to be retrieved.
        /// </param>
        /// <param name="listId">
        /// The identifier of the list containing the view to be retrieved.
        /// </param>
        /// <param name="viewId">
        /// The identifier of the view to be retrieved.
        /// </param>
        /// <returns>
        /// The view identified by <paramref name="viewId"/> in the specified list, 
        /// in the specified web, in the specified site, in the specified application.
        /// </returns>
        public override View GetView(
            Guid applicationId,
            Guid siteId,
            Guid webId,
            Guid listId,
            Guid viewId)
        {
            View view = null;

            SPWebApplication sharepointApplication = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite sharepointSite = SP2007ColumnOrderingService.FindSite(
                sharepointApplication, siteId);
            SPWeb sharepointWeb = sharepointSite.AllWebs[webId];
            SPList sharepointList = sharepointWeb.Lists[listId];
            SPView sharepointView = sharepointList.Views[viewId];

            view = ViewConverter.Convert(sharepointView);

            return view;
        }

        /// <summary>
        /// Gets the fields for the list identified by the given identifier.
        /// </summary>
        /// <param name="listId">
        /// The identifier of the list owning the fields to be retrieved.
        /// </param>
        /// <returns>
        /// The fields owned by the list identified by <paramref name="listId"/>.
        /// </returns>
        public override FieldCollection GetFields(Guid listId)
        {
            FieldCollection fieldCollection = null;

            SPList list = SPContext.Current.Web.Lists[listId];

            fieldCollection = FieldConverter.Convert(list.Fields);

            return fieldCollection;
        }

        /// <summary>
        /// Gets the fields for the specified list identifier and view identifier.
        /// </summary>
        /// <param name="listId">
        /// The identifier of the list owning the view identified by <paramref name="viewId"/>.
        /// </param>
        /// <param name="viewId">
        /// The identifier of the view whose fields will be retrieved.
        /// </param>
        /// <returns>
        /// The fields owned by the view identified by <paramref name="viewId"/>.
        /// </returns>
        public override FieldCollection GetFields(Guid listId, Guid viewId)
        {
            FieldCollection fieldCollection = null;

            SPList list = SPContext.Current.Web.Lists[listId];
            SPView view = list.Views[viewId];

            fieldCollection = FieldConverter.Convert(view.ViewFields, list.Fields);

            return fieldCollection;
        }

        /// <summary>
        /// Gets the fields for the specified list which are not included in the given view.
        /// </summary>
        /// <param name="listId">
        /// The identifier of the list owning the view identified by <paramref name="viewId"/>.
        /// </param>
        /// <param name="viewId">
        /// The identifier of the view containing the fields to be 'subtracted'.
        /// </param>
        /// <returns>
        /// The fields in the list identified by <paramref name="listId"/>,
        /// but included in the view identified by <paramref name="viewId"/>.
        /// </returns>
        public override FieldCollection GetFieldsInListNotIncludedInView(Guid listId, Guid viewId)
        {
            FieldCollection fieldCollection = new FieldCollection();

            FieldCollection listFields = this.GetFields(listId);
            FieldCollection viewFields = this.GetFields(listId, viewId);

            IDictionary<Guid, Field> viewFieldIndex = new Dictionary<Guid, Field>();

            foreach (Field viewField in viewFields)
            {
                viewFieldIndex[viewField.Id] = viewField;
            }

            foreach (Field listField in listFields)
            {
                if (!(viewFieldIndex.ContainsKey(listField.Id)))
                {
                    fieldCollection.Add(listField);
                }
            }

            return fieldCollection;
        }

        /// <summary>
        /// Updates the field order in the view identified by the specified identifiers.
        /// </summary>
        /// <param name="applicationId">
        /// The identifier of the application containing the view whose fields will be retrieved.
        /// </param>
        /// <param name="siteId">
        /// The identifier of the site containing the view whose fields will be retrieved.
        /// </param>
        /// <param name="webId">
        /// The identifier of the web containing the view whose fields will be retrieved.
        /// </param>
        /// <param name="listId">
        /// The identifier of the list containing the view whose fields will be retrieved.
        /// </param>
        /// <param name="viewId">
        /// The identifier of the view whose fields will be retrieved.
        /// </param>
        /// <param name="fields">
        /// The fields, in the new order.
        /// </param>
        public override void UpdateViewFieldOrder(
            Guid applicationId,
            Guid siteId,
            Guid webId,
            Guid listId,
            Guid viewId,
            FieldCollection fields)
        {
            SPWebApplication application = SPWebService.ContentService.WebApplications[
                applicationId];
            SPSite site = SP2007ColumnOrderingService.FindSite(application, siteId);
            SPWeb web = site.AllWebs[webId];
            SPList list = web.Lists[listId];
            SPView view = list.Views[viewId];

            this.UpdateViewFieldOrder(view, fields);
        }

        /// <summary>
        /// Updates the field order in the view identified by the specified identifiers.
        /// </summary>
        /// <param name="listId">
        /// The identifier of the list containing the view whose fields will be retrieved.
        /// </param>
        /// <param name="viewId">
        /// The identifier of the view whose fields will be retrieved.
        /// </param>
        /// <param name="fields">
        /// The fields, in the new order.
        /// </param>
        public override void UpdateViewFieldOrder(
            Guid listId, Guid viewId, FieldCollection fields)
        {
            SPView view = SPContext.Current.Web.Lists[listId].Views[viewId];

            this.UpdateViewFieldOrder(view, fields);
        }

        #endregion

        #region Methods - Instance Member - ColumnOrderingService Members

        #endregion

        #region Methods - Instance Member - SP2007ColumnOrderingService Members

        /// <summary>
        /// Updates the field order in the specified view.
        /// </summary>
        /// <param name="view">
        /// The view whose fields will be retrieved.
        /// </param>
        /// <param name="fields">
        /// The fields, in the new order.
        /// </param>
        private void UpdateViewFieldOrder(SPView view, FieldCollection fields)
        {
            view.ViewFields.DeleteAll();

            foreach (Field field in fields)
            {
                view.ViewFields.Add(field.Name);
            }

            view.Update();
        }

        #endregion

        #endregion

    }

}
